{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9f8c081-1548-4247-83d9-82d01f355808",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sisl as si\n",
    "import numpy as np\n",
    "\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cb92c41-a795-4cf6-93b3-f9e7431a944e",
   "metadata": {},
   "source": [
    "# Defining orbitals\n",
    "\n",
    "Orbitals, and basis sets, is a complicated matter that requires a broader set of classes.\n",
    "sisl enables one to use orbitals without information, but also other specialized orbitals, such as atomic orbitals and Gaussian/Slater type orbitals.\n",
    "Information about the different orbitals can be found [here](../../api/basic.html#basic-orbitals).\n",
    "\n",
    "------\n",
    "\n",
    "In this tutorial we will show how one can create different orbitals, and use them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00a3fd65-a0a1-407f-8a69-50a3f53b38b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "orb = si.Orbital(1.2, q0=1)\n",
    "print(orb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f582b010-df90-45b4-a765-65ed0ad11c9c",
   "metadata": {},
   "source": [
    "All orbitals will have some idea of its *range*. I.e. the effective range at which it acts on something. The ranges are used in `Geometry` objects to estimate which atoms interacts with other atoms, and as such they are the back-bone of tight-binding models.  \n",
    "The above orbital has a range of 1.2 Ang, and an initial charge of 1 electron.\n",
    "\n",
    "----\n",
    "\n",
    "## Orbitals with spherical shapes\n",
    "\n",
    "Many other orbitals has some shape in real space. Here we will explore two such orbitals in `sisl`.  \n",
    "In this case we will populate the orbital with an exponential decaying shape (non-physical, but instructive).\n",
    "\n",
    "Here we define the orbital range as the maximum `R` such that integral:\n",
    "$$\n",
    "\\int^R |f(r)| dr\n",
    "$$\n",
    "contains $99\\%$ of the function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4aaff8f-acd4-4c75-adab-6c401230d65d",
   "metadata": {},
   "outputs": [],
   "source": [
    "r = np.linspace(0, 3, 200)\n",
    "f = np.exp(-2 * r**2)\n",
    "sorb = si.SphericalOrbital(1, (r, f), R={\"contains\": 0.99})\n",
    "print(sorb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80cb0dc0-cd65-4fa1-bdfa-541c0534dd9d",
   "metadata": {},
   "source": [
    "Now we have a spherical orbital with $l=1$ quantum number. Lets plot its spherical form and its wavefunction:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "393dd996-fcd9-431b-a6e0-c0b40de3f6af",
   "metadata": {
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [],
   "source": [
    "for m in (0, 1):\n",
    "    # Plotting for theta = phi = 45 angles\n",
    "    plt.plot(r, sorb.psi_spher(r, 45, 45, m=m), label=f\"m={m}\")\n",
    "plt.legend();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c72e4a3b-391f-4553-ba7d-80d3187551d2",
   "metadata": {},
   "source": [
    "Note how the wavefunction gets truncated at the orbital radius, based on the truncation optimization.\n",
    "\n",
    "---\n",
    "\n",
    "The `SphericalOrbital` is typically just a temporary orbital array used for creating proper atomic orbitals. Atomic orbitals contains relevant quantum numbers, but also a spherical function. The `AtomicOrbital` accepts many other possibilities of arguments, please refer to its documentation for detailed explanations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fbe1812-3ab1-4984-8ff6-f19b3982205e",
   "metadata": {},
   "outputs": [],
   "source": [
    "aorb = si.AtomicOrbital(\"pz\", spherical=sorb)\n",
    "plt.plot(r, aorb.psi_spher(r, 45, 45));"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "958ce030-6035-41dc-b72d-2704235941ee",
   "metadata": {},
   "source": [
    "## Atoms with orbitals\n",
    "\n",
    "Atoms are defined with 1 or more orbitals. To create an atom with a specific set of orbitals simply do: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d28c9523-4d90-49ab-b918-805d4c89a989",
   "metadata": {},
   "outputs": [],
   "source": [
    "C = si.Atom(6, [sorb, aorb])\n",
    "print(C)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "542d214e-780d-44b7-bcca-81614bd2a4e2",
   "metadata": {},
   "source": [
    "This atom can then further be used in `Geometry` creations."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
